# -*- coding: utf-8 -*-
"""
基于机器学习鸢尾花分类研究-代码


【模块级别标注】
这是一个完整的机器学习项目，实现了鸢尾花分类任务。
包含数据加载、探索性分析、预处理、模型训练和评估等完整流程。
使用三种不同的机器学习算法进行对比实验。
主要步骤：
1. 数据加载和探索
2. 数据预处理
3. 模型训练
4. 模型评估
5. 结果比较
"""

# 1. 导入所需的库
# 【语句块标注：导入必要的Python库】
# 这些库提供了数据处理、可视化和机器学习的基础功能
import numpy as np  # 数值计算库，用于数组操作和数学运算
import pandas as pd  # 数据处理库，用于DataFrame操作和数据清洗
import matplotlib.pyplot as plt  # 绘图库，用于数据可视化和图表生成
import seaborn as sns  # 基于matplotlib的统计绘图库，提供更美观的图表
from sklearn import datasets  # sklearn内置数据集，包含经典机器学习数据集
from sklearn.model_selection import train_test_split  # 数据集划分工具，用于分割训练集和测试集
from sklearn.preprocessing import StandardScaler  # 特征标准化，使不同特征具有相同尺度
from sklearn.linear_model import LogisticRegression  # 逻辑回归模型，线性分类器
from sklearn.svm import SVC  # 支持向量机模型，强大的非线性分类器
from sklearn.tree import DecisionTreeClassifier  # 决策树模型，基于树结构的分类器
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix  # 模型评估指标

# 【语句块标注：设置中文字体显示】
# 解决matplotlib显示中文乱码问题
plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置中文字体为黑体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示为方块的问题

# 2. 加载数据
print("步骤1：加载鸢尾花数据集...")
# 【语句级别标注：加载sklearn内置的鸢尾花数据集】
iris = datasets.load_iris()  # 返回包含数据和标签的Bunch对象
X = iris.data  # 特征矩阵，形状为(150, 4)，包含150个样本的4个特征
y = iris.target  # 目标变量，包含3个类别的整数标签(0,1,2)
feature_names = iris.feature_names  # 特征名称列表：花萼长度、花萼宽度、花瓣长度、花瓣宽度
target_names = iris.target_names  # 类别名称列表：setosa, versicolor, virginica

# 【语句块标注：数据基本信息输出】
print("数据集形状：", X.shape)  # 输出数据集维度信息，应该是(150, 4)
print("特征名称：", feature_names)  # 显示四个特征的名字
print("类别名称：", target_names)  # 显示三个类别的名字
print("\n")  # 打印空行，使输出更清晰

# 3. 探索性数据分析
print("步骤2：探索性数据分析...")
# 【语句块标注：数据转换和准备】
# 将numpy数组转换为pandas DataFrame，便于数据分析和可视化
df = pd.DataFrame(X, columns=feature_names)  # 创建特征DataFrame，列名为特征名称
df['species'] = y  # 添加数值标签列，便于后续分析
df['species_name'] = [target_names[i] for i in y]  # 添加类别名称列，便于可视化时显示具体类别名

# 【语句块标注：数据概览输出】
print("数据前5行：")
print(df.head())  # 显示数据前5行，了解数据结构和内容
print("\n数据描述：")  # 打印描述性统计的标题
print(df.describe())  # 显示数据的统计描述信息（计数、均值、标准差、最小值、四分位数、最大值）
print("\n")  # 打印空行分隔不同部分

# 【语句块标注：数据可视化分析】
print("生成特征关系图...")
# 使用pairplot绘制特征间的关系散点图矩阵，可以直观看到特征之间的关系和类别分布
sns.pairplot(df, hue='species_name', palette='viridis')  # 按类别着色，使用viridis配色方案
plt.suptitle('鸢尾花数据集特征关系图', y=1.02, fontsize=16)  # 设置主标题，y参数调整标题位置
plt.savefig('特征关系图.png', dpi=300, bbox_inches='tight')  # 保存高清图片，dpi设置分辨率
plt.show()  # 显示图形

# 4. 数据预处理
print("步骤3：数据预处理...")
# 【语句块标注：数据集划分】
# 将数据集划分为训练集和测试集，测试集占比20%，用于评估模型泛化能力
X_train, X_test, y_train, y_test = train_test_split(
    X, y,
    test_size=0.2,  # 测试集比例20%，即30个测试样本
    random_state=42,  # 随机种子，保证每次划分结果相同，便于结果复现
    stratify=y  # 分层抽样，保持训练集和测试集中各类别比例与原数据集一致
)
print("训练集大小：", X_train.shape)  # 输出训练集维度，应该是(120, 4)
print("测试集大小：", X_test.shape)  # 输出测试集维度，应该是(30, 4)

# 【语句块标注：特征标准化】
# 对特征进行标准化处理，使每个特征均值为0，方差为1，避免某些特征因尺度较大而主导模型
scaler = StandardScaler()  # 创建标准化器对象
X_train_scaled = scaler.fit_transform(X_train)  # 在训练集上计算均值和标准差，并进行转换
X_test_scaled = scaler.transform(X_test)  # 在测试集上使用训练集的均值和标准差进行转换
print("特征标准化完成。")  # 提示标准化过程完成
print("\n")  # 打印空行分隔不同步骤

# 5. 模型训练与评估
print("步骤4：模型训练与评估...")
# 【语句块标注：模型定义】
# 定义三个不同的分类模型进行对比实验，每个模型代表不同的机器学习思想
models = {
    '逻辑回归': LogisticRegression(random_state=42),  # 线性分类模型，简单快速，适合线性可分数据
    '支持向量机': SVC(kernel='rbf', random_state=42),  # 使用径向基核函数的SVM，能处理非线性问题
    '决策树': DecisionTreeClassifier(random_state=42)  # 树形分类模型，易于理解和解释
}

# 用于存储每个模型准确率的字典，便于后续比较
results = {}

# 【语句块标注：模型训练和评估循环】
# 对每个模型进行训练、预测和评估，比较不同模型的性能
for name, model in models.items():
    print(f"--- 正在训练 {name} 模型 ---")  # 提示当前正在训练的模型

    # 训练模型：在标准化后的训练数据上拟合模型
    model.fit(X_train_scaled, y_train)  # 使用训练数据训练模型

    # 模型预测：在测试集上进行预测，评估模型泛化能力
    y_pred = model.predict(X_test_scaled)  # 使用训练好的模型对测试集进行预测

    # 性能评估：计算模型在测试集上的准确率
    accuracy = accuracy_score(y_test, y_pred)  # 比较预测结果和真实标签，计算准确率
    results[name] = accuracy  # 存储准确率结果到字典中

    print(f"{name} 测试集准确率: {accuracy:.4f}")  # 输出准确率，保留4位小数
    print(f"{name} 分类报告：")  # 输出分类报告标题
    # 输出详细的分类评估报告，包括精确率、召回率、F1分数等指标
    print(classification_report(y_test, y_pred, target_names=target_names))

    # 【语句块标注：混淆矩阵可视化】
    # 计算混淆矩阵，显示模型在每个类别上的预测情况
    cm = confusion_matrix(y_test, y_pred)  # 计算混淆矩阵

    # 绘制热力图形式的混淆矩阵，直观显示分类结果
    plt.figure(figsize=(6, 5))  # 设置图形大小
    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',  # 热力图参数：显示数值、整数格式、蓝色配色
                xticklabels=target_names, yticklabels=target_names)  # 设置坐标轴标签为类别名
    plt.title(f'{name}模型 - 混淆矩阵', fontsize=14, fontweight='bold')  # 设置标题
    plt.ylabel('真实标签', fontsize=12)  # 设置y轴标签
    plt.xlabel('预测标签', fontsize=12)  # 设置x轴标签
    plt.xticks(rotation=45)  # x轴标签旋转45度，避免重叠
    plt.yticks(rotation=0)  # y轴标签不旋转
    plt.savefig(f'{name}_混淆矩阵.png', dpi=300, bbox_inches='tight')  # 保存混淆矩阵图片
    plt.show()  # 显示图形
    print("\n")  # 打印空行分隔不同模型的结果

# 6. 结果比较
print("步骤5：模型结果比较...")
print("\n=== 最终模型准确率对比 ===")  # 输出结果比较标题
# 【语句块标注：结果汇总和展示】
# 遍历结果字典，输出每个模型的准确率
for name, acc in results.items():
    print(f"{name}: {acc:.4f}")  # 格式化输出模型名称和准确率

# 【语句块标注：最佳模型选择】
# 找出准确率最高的模型，用于后续分析或实际应用
best_model_name = max(results, key=results.get)  # 通过准确率选择最佳模型
best_model_accuracy = results[best_model_name]  # 获取最佳模型的准确率
print(f"\n** 最佳模型是 {best_model_name}，准确率为 {best_model_accuracy:.4f} **")  # 输出最佳模型信息

# 【语句块标注：结果可视化比较】
# 绘制准确率对比柱状图，直观显示各模型性能差异
plt.figure(figsize=(10, 6))  # 设置图形大小
models_list = list(results.keys())  # 模型名称列表
accuracy_list = list(results.values())  # 准确率数值列表

# 创建颜色列表，最佳模型用不同颜色突出显示
colors = []  # 初始化颜色列表
for model in models_list:  # 遍历所有模型
    if model == best_model_name:  # 如果是最佳模型
        colors.append('gold')  # 最佳模型用金色标记，突出显示
    else:  # 其他模型
        colors.append('lightblue')  # 其他模型用浅蓝色

# 绘制柱状图
bars = plt.bar(models_list, accuracy_list, color=colors,
               edgecolor='black', linewidth=1.2)  # 设置边框颜色和宽度
plt.title('三种机器学习模型在鸢尾花分类上的准确率对比',
          fontsize=16, fontweight='bold', pad=20)  # 设置主标题
plt.ylabel('准确率', fontsize=14)  # 设置y轴标签
plt.xlabel('机器学习模型', fontsize=14)  # 设置x轴标签
plt.ylim(0.8, 1.0)  # 设置y轴范围，使准确率差异更明显

# 设置坐标轴标签样式
plt.tick_params(axis='both', which='major', labelsize=12)  # 设置刻度标签字体大小

# 在柱子上显示准确率数值，便于精确读取
for bar, acc in zip(bars, accuracy_list):  # 遍历每个柱子和对应的准确率
    plt.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 0.005,
             f'{acc:.4f}', ha='center', va='bottom',
             fontsize=12, fontweight='bold')  # 在柱子顶部显示准确率数值

# 添加网格线便于阅读
plt.grid(axis='y', alpha=0.3, linestyle='--')  # 只显示y轴网格线，设置透明度和线型

# 添加图例说明最佳模型
plt.legend([bars[models_list.index(best_model_name)]], ['最佳模型'],  # 图例内容
           loc='upper right', frameon=True)  # 图例位置和边框设置

plt.savefig('模型准确率对比.png', dpi=300, bbox_inches='tight')  # 保存对比图
plt.show()  # 显示图形

print("\n程序运行结束！所有图表已保存。")  # 提示程序执行完成